function mdl = cache(mdl, cachelevel, cov_shared)
%CACHE Re-cache the model pre-computation according to the specified level
%
% [ Syntax ]
%   - mdl = cache(mdl, cachelevel)
%   - mdl = cache(mdl, cachelevel, cov_shared)
%
% [ Description ]
%   - mdl = cache(mdl, cachelevel) redo the caching of pre-computation
%     according to the specified level. 
%
%     The cache levels are listed below.
%       \{:
%           - 0:        cache nothing
%           - 1:        cache useful scalars, 
%                       including log(det(cov)), trace(cov), and 
%                       mu^T cov^{-1} mu
%           - 2:        cache useful vectors in addition to the above
%                       including cov^{-1} mu, and inverse of the diagonal
%                       elements of covariance.
%       \:}
%     
%     This function release the quantities that are not required in 
%     the specified level and caches the required quantities that have
%     not been cached.
%
%   - mdl = cache(mdl, cachelevel, cov_shared) to tell the function that
%     all models actually share the same covariance.
%
% [ History ]
%   - Created by Dahua Lin, on Dec 24, 2007
%


%% parse and verify input

assert(isnumeric(cachelevel) && isscalar(cachelevel) && cachelevel >= 0, ...
    'sltoolbox:slcdiaggauss:cachelevel', ...
    'cachelevel should be a nonnegative scalar.');

if nargin < 3
    cov_shared = false;
end


%% decide cache plan

switch cachelevel
    case 0
        mdl = cache_l0(mdl);   
    case 1
        mdl = cache_l1(mdl, cov_shared);
    otherwise
        mdl = cache_l2(mdl, cov_shared);
end



%% core function

% Level 0

function mdl = cache_l0(mdl)

K = numel(mdl);

if K == 1
    mdl.clevel = 0;
    mdl.ldsm = [];
    mdl.trsm = [];
    mdl.invsm = [];
    mdl.ism_mu = [];
    mdl.mu_ism_mu = [];
    
else
    for i = 1 : K
        mdl(i) = cache_l0(mdl(i));
    end
end


% Level 1

function mdl = cache_l1(mdl, cov_shared)

K = numel(mdl);

if K == 1
    mdl.clevel = 1;
    
    if isempty(mdl.ldsm)
        mdl.ldsm = sum(log(mdl.sigma));
    end
    
    if isempty(mdl.trsm)
        mdl.trsm = sum(mdl.sigma);
    end
    
    mdl.invsm = [];
    mdl.ism_mu = [];
    
    if isempty(mdl.mu_ism_mu)
        mdl.mu_ism_mu = mdl.mu' * (mdl.mu ./ mdl.sigma);
    end
    
elseif cov_shared
    
    if isempty(mdl(1).ldsm)
        ldsm = sum(log(mdl(1).sigma));
    else
        ldsm = mdl(1).ldsm;
    end
    
    if isempty(mdl(1).trsm)
        trsm = sum(mdl(1).sigma);
    else
        trsm = mdl(1).trsm;
    end
    
    if isempty(mdl(1).invsm)
        invsm = 1 ./ mdl(1).sigma;
    else
        invsm = mdl(1).invsm;
    end
    
    for i = 1 : K
        mdl(i).clevel = 1;
        mdl(i).ldsm = ldsm;
        mdl(i).trsm = trsm;
        mdl(i).invsm = [];
        mdl(i).ism_mu = [];
        if isempty(mdl(i).mu_ism_mu)
            mdl(i).mu_ism_mu = mdl(i).mu' * (mdl(i).mu .* invsm);
        end
    end
    
else
    for i = 1 : K
        mdl(i) = cache_l1(mdl(i));
    end
end


% Level 2

function mdl = cache_l2(mdl, cov_shared)

K = numel(mdl);

if K == 1
    mdl.clevel = 2;
    
    if isempty(mdl.ldsm)
        mdl.ldsm = sum(log(mdl.sigma));
    end
    
    if isempty(mdl.trsm)
        mdl.trsm = sum(mdl.sigma);
    end
    
    if isempty(mdl.invsm)
        mdl.invsm = 1 ./ mdl.sigma;
    end
    
    if isempty(mdl.ism_mu)
        mdl.ism_mu = mdl.mu .* mdl.invsm;
    end
    
    if isempty(mdl.mu_ism_mu)
        mdl.mu_ism_mu = mdl.mu' * mdl.ism_mu;
    end
    
elseif cov_shared
    
    if isempty(mdl(1).ldsm)
        ldsm = sum(log(mdl(1).sigma));
    else
        ldsm = mdl(1).ldsm;
    end
    
    if isempty(mdl(1).trsm)
        trsm = sum(mdl(1).sigma);
    else
        trsm = mdl(1).trsm;
    end
    
    if isempty(mdl(1).invsm)
        invsm = 1 ./ mdl(1).sigma;
    else
        invsm = mdl(1).invsm;
    end
    
    for i = 1 : K
        mdl(i).clevel = 2;
        mdl(i).ldsm = ldsm;
        mdl(i).trsm = trsm;
        mdl(i).invsm = invsm;
        
        if isempty(mdl(i).ism_mu)
            mdl(i).ism_mu = mdl(i).mu .* invsm;
        end
        
        if isempty(mdl(i).mu_ism_mu)
            mdl(i).mu_ism_mu = mdl(i).mu' * mdl(i).ism_mu;
        end
    end
    
else
    for i = 1 : K
        mdl(i) = cache_l2(mdl(i));
    end
end

          